#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import "GeoPoint.h";
#import "LocLib.h"
#import "CoreTelephony.h"
#import "Celltower.h"

/*
    For some reason, all these variables have to be global. Toolchain bug?
*/
CFMachPortRef port;
struct CTServerConnection *sc=NULL;
struct CellInfo cellinfo;
int b;
int t1;

int min(int a, int b){
	if (a<b)
		return a;
	return b;
}
void callback()
{
    printf("Callback called\n");
}


/*
    Again, _CTServerConnectionCreate() has to be called outside the main(), or whatever is using CTServerConnection. 
    Hence the existence of this function.
*/

void cellconnect()
{
//    int t1;
    sc=_CTServerConnectionCreate(kCFAllocatorDefault, callback, NULL);

    /*
        port is not currently used, shuld be usable with a runloop.
    */
    port=CFMachPortCreateWithPort(kCFAllocatorDefault, _CTServerConnectionGetPort(sc), NULL, NULL, NULL);

    _CTServerConnectionCellMonitorStart(&t1,sc);

    printf("Connected\n");
}


NSArray *getCellInfo3()
{
	NSArray *r = [[[NSMutableArray alloc] init] autorelease];	
    int cellcount;
    _CTServerConnectionCellMonitorGetCellCount(&t1,sc,&cellcount);

    printf("Cell count: %x\n",cellcount);

    printf("Size = %x\n", sizeof(struct CellInfo));

    unsigned char *a=malloc(sizeof(struct CellInfo));
//    unsigned char *tt=malloc(sizeof(struct CellInfo));
//	struct CellInfo cellinfo;

    for(b=0;b<cellcount;b++)
    {
        //OMG the toolchain is broken, &cellinfo doesn't work
        _CTServerConnectionCellMonitorGetCellInfo(&t1,sc,b,a); 
		memcpy(&cellinfo,a,sizeof(struct CellInfo));
//		memcpy(arr+b,&cellinfo,sizeof(struct CellInfo));
        //OMG the toolchain is more broken, these printfs don't work on one line
        printf("Cell Site: %d, MNC: %d, ",b,cellinfo.servingmnc);
        printf("Location: %d, Cell ID: %d, Station: %d, ",cellinfo.location, cellinfo.cellid, cellinfo.station);
        printf("Freq: %d, RxLevel: %d, ", cellinfo.freq, cellinfo.rxlevel);
        printf("C1: %d, C2: %d\n", cellinfo.c1, cellinfo.c2);
//		CellTower *t = [[CellTower alloc] initWith:&cellinfo];
//		t = [[CellTower alloc] initWithId:0 andLoc:0 andPow:0];
//		t = [[CellTower alloc] initWithId:cellinfo.cellid andLoc:cellinfo.location andPow:cellinfo.rxlevel];
//		t = [NSObject alloc];
//		[r addObject:t];
    }
	struct CellInfo *arr = malloc(cellcount * sizeof(struct CellInfo));
    if(a) free(a);
    for(b=0;b<cellcount;b++)
    {
        printf("Cell Site: %d, MNC: %d, ",b,arr[b].servingmnc);
        printf("Location: %d, Cell ID: %d, Station: %d, ",arr[b].location, arr[b].cellid, arr[b].station);
        printf("Freq: %d, RxLevel: %d, ", arr[b].freq, arr[b].rxlevel);
        printf("C1: %d, C2: %d\n", arr[b].c1, arr[b].c2);
	}
	free(arr);
	return r;
}


id getCellInfo2()
{
	NSMutableArray *r = [[NSMutableArray alloc] init];	
	CellTower *t;
    int cellcount;
	struct CellInfo *all = malloc(20 * sizeof(struct CellInfo));
    unsigned char *a=malloc(sizeof(struct CellInfo));
    _CTServerConnectionCellMonitorGetCellCount(&t1,sc,&cellcount);

    printf("Cell count: %x\n",cellcount);

    printf("Size = %x\n", sizeof(struct CellInfo));
	cellcount = min(20,cellcount);
//	struct CellInfo cellinfo;
    for(b=0;b<cellcount;b++)
    {
        //OMG the toolchain is broken, &cellinfo doesn't work
        _CTServerConnectionCellMonitorGetCellInfo(&t1,sc,b,a); 
		memcpy(&cellinfo,a,sizeof(struct CellInfo));
		memcpy(all + b , a,sizeof(struct CellInfo));
        //OMG the toolchain is more broken, these printfs don't work on one line
//        printf("Cell Site: %d, MNC: %d, ",b,cellinfo.servingmnc);
		printf("%x %x %x\n", all, &cellinfo, a);
        printf("Cell Site: %d, MNC: %d, ",b,all->servingmnc);
        printf("Location: %d, Cell ID: %d, Station: %d, ",cellinfo.location, cellinfo.cellid, cellinfo.station);
        printf("Freq: %d, RxLevel: %d, ", cellinfo.freq, cellinfo.rxlevel);
        printf("C1: %d, C2: %d\n", cellinfo.c1, cellinfo.c2);
	
    }
    if(a) free(a);
    for(b=0;b<cellcount;b++)
    {
        printf("Cell Site: %d, MNC: %d, ",b,all[b].servingmnc);
        printf("Location: %d, Cell ID: %d, Station: %d, ",all[b].location, all[b].cellid, all[b].station);
        printf("Freq: %d, RxLevel: %d, ", all[b].freq, all[b].rxlevel);
        printf("C1: %d, C2: %d\n", all[b].c1, all[b].c2);
		t = [[CellTower alloc] initWith:(all + b)];
  //		t = [[CellTower alloc] initWithId:0 andLoc:0 andPow:0];
		[r addObject:t];
	}
	free(all);
	return [r autorelease];
}




NSData *post(NSString *url,NSData *body) {
	
	NSURL *nsurl = [[[NSURL alloc] initWithString:url] autorelease];
	CFURLRef _uploadURL = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)url, NULL);
	CFHTTPMessageRef _request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), _uploadURL, kCFHTTPVersion1_1);
	CFRelease(_uploadURL);
	_uploadURL = NULL;
	
	CFHTTPMessageSetHeaderFieldValue(_request, CFSTR("Content-Type"), CFSTR("application/binary"));
	CFHTTPMessageSetHeaderFieldValue(_request, CFSTR("Host"), (CFStringRef)[nsurl host]);
	CFHTTPMessageSetHeaderFieldValue(_request, CFSTR("Content-Length"), (CFStringRef)[NSString stringWithFormat: @"%d", [body length]]);
	CFHTTPMessageSetBody(_request, (CFDataRef)body);
	
	
	CFReadStreamRef _readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, _request);
	
	
	BOOL err = CFReadStreamOpen(_readStream);
    // check for posible errors
	//UInt32 myErrCode = CFHTTPMessageGetResponseStatusCode(myResponse);
	// myErrCode has now the error code
//	NSLog(@"Error code:%d",myErrCode);	
	NSMutableData *responseString = [[NSMutableData alloc] init];
	CFIndex numBytesRead;
	long bytesWritten, previousBytesWritten = 0;
	UInt8 buf[1024];
	BOOL doneUploading = NO;


	
	while (!doneUploading) {
		CFNumberRef cfSize = CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount);
		CFNumberGetValue(cfSize, kCFNumberLongType, &bytesWritten);
		CFRelease(cfSize);
		cfSize = NULL;
		
		if (bytesWritten > previousBytesWritten) {
			previousBytesWritten = bytesWritten;
			NSNumber *progress = [NSNumber numberWithFloat: ((float)bytesWritten / (float)[body length])];
			// This is the spot to add a callback function
			NSLog(@"Upload:%@\n",progress);
			//[_pushr performSelectorOnMainThread: @selector(updateProgress:) withObject: progress waitUntilDone: YES];
		}
		
		if (!CFReadStreamHasBytesAvailable(_readStream)) {
			usleep(3600);
			continue;
		}
		
		numBytesRead = CFReadStreamRead(_readStream, buf, 1024);
		if (numBytesRead < 0){
			NSLog(@"Error");
			return nil;
		}
		if (numBytesRead < 1024)
			buf[numBytesRead] = 0;
		[responseString appendBytes:buf length:numBytesRead];
		
		if (CFReadStreamGetStatus(_readStream) == kCFStreamStatusAtEnd) doneUploading = YES;
	}
	
	
//	CFHTTPMessageRef myResponse =(CFHTTPMessageRef) CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPResponseHeader);
	
	CFReadStreamClose(_readStream);
	CFRelease(_request);
	_request = NULL;
	CFRelease(_readStream);
	_readStream = NULL;
	
	return [responseString autorelease];
}



int writeShort(int sh, char *srt,int place){
	srt[place]=(sh>>8)&0xff;
	srt[place+1]=sh & 0xff;
	return place+2;
}
int writeInt(int sh, char *srt,int place){
	srt[place]=(sh>>24)&0xff;
	srt[place+1]=(sh>>16)&0xff;
	srt[place+2]=(sh>>8)&0xff;
	srt[place+3]=sh & 0xff;
	return place+4;
}
int writeUTF(char *dsc, char *str, int start){
	int i;
	start = writeShort(strlen(str),dsc,start);
	for (i=0;i<strlen(str);i++){
		dsc[start+i]=str[i];
	}
	return start + i;
}

int readInt(char *data,int place){
	int r = 0;
	r = (0xff & data[place+0])<<24 | (0xff & data[place+1])<<16 | (0xff & data[place+2])<<8 | (0xff & data[place+3]);
	return r;
}

GeoPoint *getLocation(NSData *resp){
	double lat;
	double lon;
	if ([resp length]<25)
		return nil;	
	char data[25];
	[resp getBytes:data];
	lat = readInt(data,7)/1000000.0;
	lon = readInt(data,11)/1000000.0;
	GeoPoint *p = [[GeoPoint alloc] initwithLat:lat andLong:lon];
	return p;
}

NSData *makePostData(int a, int b){

	int l = 85;
	char datapost[l];
	int i;
	int place;
	for (i=0;i<l;i++){
		datapost[i]=0;
	}
	datapost[1]=(char)0x15;
	place = writeUTF(datapost, "fr", 10);
	place = writeUTF(datapost, "Sony_Ericsson-K750", place);
	place = writeUTF(datapost, "1.3.1", place);
	place = writeUTF(datapost, "web", place);
	datapost[place]=0x1b;
	place++;
	place +=8;
	place = writeInt(3, datapost, place);
	place = writeShort(0, datapost, place);
	place = writeInt(a, datapost, place); // 22251
	place = writeInt(b, datapost, place);  // 5013
	place+=16;
	//datapost[place++]=0x0d;
	//datapost[place++]=0x0a;
	
		
	printf("\n");
	
	NSData *d = [NSData dataWithBytes:datapost length:place];
	return d;

}


GeoPoint *queryLocation(int tid, int location){
	NSData *data = makePostData(tid, location);
	NSLog(@"Made the data");
	NSData *response = post(@"http://google.com/glm/mmap", data);
	if (response == nil)
		return nil;
	GeoPoint *p = getLocation(response);
	return p;
}

GeoPoint *getBestTower(){
	NSArray *l = getCellInfo2();
	if (l == nil)
        return nil;
    int i=0;
	float power = -100.0;
	CellTower *best = nil;	
    for (i=0;i<[l count];i++){
        CellTower *t  = [l objectAtIndex:i];
		if (best == nil || [t power]>[best power]){
			best = t;
		}	
    }
	
    GeoPoint *p = queryLocation([best towerId],[best towerLoc]);
	return p;
}

NSArray *getTowers(){
	NSArray *l = getCellInfo2();	
	if (l == nil)
		return nil;
	int i=0;
	for (i=0;i<[l count];i++){
		CellTower *t  = [l objectAtIndex:i];
		GeoPoint *p = queryLocation([t towerId],[t towerLoc]);
		NSLog(@"%f %f",[p latitude],[p longitude]);
		[t setLocation:p];
	}
	return l;
}
